package typesys;

import java.util.ArrayList;
import java.util.HashSet;

import ast.Principal;

public class PrincipalHierarchy {
	
	private ArrayList<Principal> principals;
	
	public PrincipalHierarchy(ArrayList<Principal> prs)
	{
		this.principals = prs;
	}
	
	public ArrayList<Principal> getPrincipals()
	{
		return principals;
	}
	
	public PrincipalHierarchy deepCopy()
	{
		ArrayList<Principal> newPrincipals = new ArrayList<Principal>();
		for (Principal pr : principals) {
			newPrincipals.add(new Principal(pr.getName()));
		}
		
		PrincipalHierarchy nPh = new PrincipalHierarchy(newPrincipals);
		for (Principal pr : principals) {
			ArrayList<Principal> afs = pr.getActsFor();
			for (Principal af : afs) {
				nPh.addActsFor(nPh.findPrincipal(pr), nPh.findPrincipal(af));
			}
		}
		
		return nPh;
	}
	
	public void addActsFor(Principal p, Principal q)
	{
		Principal res = null;
		for (Principal r : principals) {
			if (r.getName().equals(p.getName())) {
				res = r;
				break;
			}
		}
		
		if (res != null) {
			res.addActFor(findPrincipal(q));
		} 
		
	}
	
	public boolean actsFor(Principal p, Principal q) {
		
		boolean result = false;
		
		if (p instanceof TopPrincipal || q instanceof BottomPrincipal ||
			p.getName().equals(q.getName()))
		{
			result = true;
			
		} else {
			HashSet<Principal> visited = new HashSet<Principal>();
			result = checkAF(visited, findPrincipal(p), findPrincipal(q));
		}
		
		return result;
	}
	
	private Principal findPrincipal(Principal pr)
	{
		for (Principal p : principals) {
			if (p.getName().equals(pr.getName())) {
				return p;
			}
		}
		
		return null;
	}
	
	private boolean checkAF(HashSet<Principal> visited, Principal p, Principal q)
	{
		boolean result = false;
		
		if (visited.contains(p))
			return result;
		
		visited.add(p);
		
		for (Principal r : p.getActsFor()) {
			
			if (r.getName().equals(q.getName())) {
				result = true;
				break;
			}
				
		}
		
		if (!result) {
			
			for (Principal r : p.getActsFor()) {
				result = checkAF(visited, r, q);
				if (result)
					break;
			}
		}
		
		return result;
	}
	
}
